package com.trainbook.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.trainbook.common.result.JsonResult;
import com.trainbook.common.tpwConstants;
import com.trainbook.entity.DTO.AddTrainDTO;
import com.trainbook.entity.DTO.TrainListDTO;
import com.trainbook.entity.DTO.middle.AddTrain_TrainSite;
import com.trainbook.entity.*;
import com.trainbook.entity.VO.TrainVO;
import com.trainbook.mapper.TrainMapper;
import com.trainbook.service.*;
import com.trainbook.utils.sort.TrainVOComparator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;

/**
 * <p>
 * 车次表 服务实现类
 * </p>
 *
 * @author Dempsey
 * @since 2020-11-27
 */
@Service
public class TrainServiceImpl extends ServiceImpl<TrainMapper, Train> implements ITrainService {

    @Autowired
    private ISite2siteService site2siteService;

    @Autowired
    private ISeatService seatService;

    @Autowired
    private ISeatItemService seatItemService;

    @Autowired
    private ITrainSitesService trainSitesService;

    /**
     * 添加一个车次信息
     *
     * @param addTrainDTO 新增车次的信息
     * @return 成功与否嘛
     */
    @Override
    @Transactional
    public JsonResult addTrain(AddTrainDTO addTrainDTO) {
        // 检查车次是否已存在

        // 以下由于将车次设为唯一的，因此弃用
        // 实际生活中，车次名可能会重复，即同一辆车，几天内多次开的话，车次都一样的，
        // 那么还要做时间的判断，做的是判断同一车次的运行时间是否有重叠部分
        // （这里忽略结束运行后到下一次运行的位移时间，就是能瞬移）

        String trianName = addTrainDTO.getTrainName();
        LocalDateTime startTime = addTrainDTO.getStartTime();

        QueryWrapper<Train> trainQueryWrapper = new QueryWrapper<>();
        trainQueryWrapper.eq("trainName", trianName);
        List<Train> trainList = list(trainQueryWrapper);

        if (trainList.size() > 0) {
            return JsonResult.error("车次已存在");
        }

//        if (trainList.size() > 0) {
//            // 先拿到一个同名车次的运行时间始末列表
//            List<Map<String, LocalDateTime>> trainTimes = new ArrayList<>();
//            for (Train train : trainList) {
//                Map<String, LocalDateTime> trainTime = new HashMap<>(69);
//                trainTime.put("startTime", train.getStartTime());
//                trainTime.put("endTime", train.getEndTime());
//                trainTimes.add(trainTime);
//            }
//            // 再根据 startTime 顺序排序
//            Collections.sort(trainTimes, Comparator.comparing(o -> o.get("startTime")));
//            // 从第二车次开始，如果改车的 startTime 在前车的运行时间段内，报错
//            for (int i = 1; i < trainTimes.size(); i++) {
//                for (int j = 0; j < i; j++) {
//                    if (LocalDateTimeUtil.ifin(trainTimes.get(i).get("startTime"),
//                            trainTimes.get(j).get("startTime"),
//                            trainTimes.get(j).get("endTime"))) {
//                        return JsonResult.error("与已存在的车次运行时间重叠");
//                    }
//                }
//            }
//        }

        // 先保存一下一个新的站点
        save(Train.builder().trainName(trianName)
                .startTime(startTime)
                .build());
        // 取得新存的站点的id
        trainQueryWrapper.eq("trainName", trianName);
        Integer id = getOne(trainQueryWrapper).getId();

        // 再添加站点关联的表项
        List<AddTrain_TrainSite> addTrain_trainSites = addTrainDTO.getAddTrain_TrainSite();
        Collections.sort(addTrain_trainSites, Comparator.comparing(AddTrain_TrainSite::getOrder));
        LocalDateTime lastArriveTime = null;
        LocalDateTime lastLeaveTime = null;
        for (int i = 0; i < addTrain_trainSites.size() - 1; i++) {
            TrainSites trainSites = new TrainSites();

            // trainId
            trainSites.setTrainId(id);

            // 最前面一站，不需要到站时间，需要离站时间
            if (i == 0) {
                trainSites.setArriveTime(null);
                lastLeaveTime = addTrainDTO.getStartTime();
                trainSites.setLeaveTime(lastLeaveTime);
            }

            // 查询上次离开到本次到站的所需时间
            int nowSite = addTrain_trainSites.get(i).getSiteId();
            int lastSite = addTrain_trainSites.get(i - 1).getSiteId();
            QueryWrapper<Site2site> site2siteQueryWrapper = new QueryWrapper<>();
            site2siteQueryWrapper.eq("siteA", nowSite).eq("siteB", lastSite)
                    .or().eq("siteA", lastSite).eq("siteB", nowSite);
            Site2site site2site = site2siteService.getOne(site2siteQueryWrapper);
            Long requiredTime = site2site.getRequiredTime();

            // 到站时间是上次离站后的所需的运行时间
            lastLeaveTime = lastLeaveTime.plusSeconds(requiredTime);
            trainSites.setArriveTime(lastLeaveTime);

            // 最后面一站 需要到站时间，不需要离站时间
            if (i == addTrain_trainSites.size()) {
                trainSites.setLeaveTime(null);
            }

            // 中间站点，需要到站时间，需要离站时间
            if (i > 0) {
                // 离站时间是本次到站后 5 分钟
                trainSites.setLeaveTime(lastArriveTime.plusSeconds(tpwConstants.WAITTIME));
            }

            // order
            trainSites.setOrder(addTrain_trainSites.get(i).getOrder());
            // waitTime
            trainSites.setWaitTime(tpwConstants.WAITTIME);
        }

        // 别忘了大明湖畔的座位啊
        List<SeatItem> seatItemList = seatItemService.list();
        for (SeatItem seatItem : seatItemList) {
            Seat seat = Seat.builder().level(seatItem.getSeatLevel())
                    .trainId(id)
                    .allNum(seatItem.getMax_num())
                    .lastNum((int) (seatItem.getMax_num() * Math.random()))
                    .build();
            seatService.save(seat);
        }

        return JsonResult.success();
    }

    /**
     * 无条件返回车次
     * 这种情况下是不考虑换乘的
     *
     * @param showTrainListDTO 查询条件
     * @return
     */
    @Override
    public JsonResult listWithoutCondition(TrainListDTO showTrainListDTO) {
        // 直接遍历 train 就完事了，拿到所有 train 的 id，然后去 trainSite 里面 ，把数据都封装成 trainVO 的形式就好了
        List<Train> trainList = list();
        List<TrainVO> trainVOList = new ArrayList<>();
        for (Train train : trainList) {
            TrainVO trainVO = new TrainVO();
            trainVO.setStartTime(train.getStartTime());
            trainVO.setEndTime(train.getEndTime());
            trainVO.setTakeTime();

            QueryWrapper<TrainSites> trainSitesQueryWrapper = new QueryWrapper<>();
            trainSitesQueryWrapper.eq("trainId", train.getId());
            List<TrainSites> trainSitesList = trainSitesService.list(trainSitesQueryWrapper);
            Collections.sort(trainSitesList, Comparator.comparing(TrainSites::getOrder));
            trainVO.setTrainSitesList(trainSitesList);

            trainVO.setSeatList(seatService.list(new QueryWrapper<Seat>().eq("trainId", train.getId())));
            trainVO.setTrainNameMap();
            trainVO.setPrice();

            trainVOList.add(trainVO);
        }
        return JsonResult.result(trainVOList);
    }

    public TrainServiceImpl() {
    }

    /**
     * 根据基本条件返回车次
     *
     * @param trainListDTO 查询条件
     * @return
     */
    @Override
    public JsonResult listWithBaseCondition(TrainListDTO trainListDTO) {
        List<TrainVO> trainVOList = new ArrayList<>();

        Integer startSiteId = trainListDTO.getStartSiteId();
        Integer endSiteId = trainListDTO.getEndSiteId();
        LocalDateTime startTime = trainListDTO.getStartTime();

        // 拿到出发时间晚于设定时间，且站点非车次最后一站的途径站点列表
        List<TrainSites> trainSitesList = trainSitesService.list(new QueryWrapper<TrainSites>()
                .eq("siteId", startSiteId).ge("leaveTime", startTime));

        // 接下来要做深度优先遍历，要在一次换乘内完成目的地到达
        for (TrainSites trainSites : trainSitesList) {
            // 获取改车次的所有站点集
            List<TrainSites> sameIdTrainSitesList = getSameIdTrainSitesListByTrainId(trainSites.getTrainId());
            // 遍历
            for (TrainSites trainSites1 : sameIdTrainSitesList) {
                // 首先判断该车次能否直接到达
                if (trainSites1.getSiteId().equals(endSiteId) && trainSites1.getOrder() > trainSites.getOrder()) {
                    // 成功过的话就是赋值的事儿了
                    TrainVO trainVO = new TrainVO();

                    // 先算中间站点列表
                    List<TrainSites> trainSites2 = new ArrayList<>();
                    trainSites2.addAll(sameIdTrainSitesList.subList(trainSites.getOrder(), trainSites1.getOrder() + 1));
                    trainVO.setTrainSitesList(trainSites2);
                    // 再搞车次名列表
                    Map<Integer, String> trainNameMap = new HashMap<>();
                    String trainName = getOne(new QueryWrapper<Train>().eq("id", trainSites.getTrainId())).getTrainName();
                    trainNameMap.put(trainSites.getTrainId(), trainName);
                    trainVO.setTrainNameMap();

                    // 然后是开始、结束时间和历时
                    trainVO.setStartTime(trainSites.getLeaveTime());
                    trainVO.setEndTime(trainSites1.getArriveTime());
                    trainVO.setTakeTime();

                    trainVO.setAllistance();
                    trainVO.setPrice();

                    trainVO.setSeatList(seatService.list(new QueryWrapper<Seat>().eq("trainId", trainSites.getTrainId())));

                    trainVOList.add(trainVO);
                }
                // 一遍不过就换乘一次

                // 现在上一次运行结束的时间就相当于 startTime
                LocalDateTime lastArriveTime = trainSites1.getArriveTime();
                // 与之相对应的，拿到出发时间晚于设定时间，且站点非车次最后一站的途径站点列表
                List<TrainSites> trainSitesList_ = trainSitesService.list(new QueryWrapper<TrainSites>()
                        .eq("siteId", trainSites1.getSiteId()).ge("leaveTime", lastArriveTime));
                // 紧接着的是做遍历在这边已经可以很明显地看出深度优先的样子了，但是我只做两次的，所以就不抽象成一个递归方法了
                for (TrainSites trainSites_ : trainSitesList_) {
                    // 获取改车次的所有站点集
                    List<TrainSites> sameIdTrainSitesList_ = getSameIdTrainSitesListByTrainId(trainSites_.getTrainId());
                    // 遍历
                    for (TrainSites trainSites1_ : sameIdTrainSitesList_) {
                        if (trainSites1_.getSiteId().equals(endSiteId) && trainSites1_.getOrder() > trainSites_.getOrder()) {
                            // 成功过的话又是赋值的事儿了，但是这次会复杂一点
                            TrainVO trainVO = new TrainVO();

                            // 先算中间站点列表
                            List<TrainSites> _trainSites2 = new ArrayList<>();
                            // 先加前一车次的，再加后一车次的
                            _trainSites2.addAll(sameIdTrainSitesList.subList(trainSites.getOrder(), trainSites1.getOrder() + 1));
                            _trainSites2.addAll(sameIdTrainSitesList.subList(trainSites_.getOrder(), trainSites1_.getOrder() + 1));
                            trainVO.setTrainSitesList(_trainSites2);
                            // 再搞车次名列表，也是要加两个
                            Map<Integer, String> trainNameMap_ = new HashMap<>();
                            String trainName_1 = getOne(new QueryWrapper<Train>().eq("id", trainSites.getTrainId())).getTrainName();
                            String trainName_2 = getOne(new QueryWrapper<Train>().eq("id", trainSites_.getTrainId())).getTrainName();
                            trainNameMap_.put(trainSites.getTrainId(), trainName_1);
                            trainNameMap_.put(trainSites.getTrainId(), trainName_2);
                            trainVO.setTrainNameMap();

                            // 然后是开始、结束时间和历时
                            trainVO.setStartTime(trainSites.getLeaveTime());
                            trainVO.setEndTime(trainSites1_.getArriveTime());
                            trainVO.setTakeTime();

                            trainVO.setAllistance();
                            trainVO.setPrice();

                            trainVO.setSeatList(seatService.list(new QueryWrapper<Seat>().eq("trainId", trainSites.getTrainId())));

                            trainVOList.add(trainVO);
                        }
                    }
                }
            }
        }

        // 排序优先级依次为，少换乘，少时间，少价格
        TrainVOComparator trainVOComparator = new TrainVOComparator();
        Collections.sort(trainVOList, trainVOComparator);

        // 过滤一遍有中间站点的
        List<Site> middleSiteList = trainListDTO.getMiddleSiteList();
        List<TrainVO> finalTrainVOList = new ArrayList<>();
        for (TrainVO trainVO : trainVOList) {
            for (TrainSites trainSites :trainVO.getTrainSitesList()) {
                for (Site site :middleSiteList) {
                    if(trainSites.getSiteId().equals(site.getId())){
                        finalTrainVOList.add(trainVO);
                    }
                }
            }
        }

        if(finalTrainVOList.size() == 0){
            return JsonResult.error("没有符合条件的车次");
        }

        return JsonResult.result(finalTrainVOList);
    }

    /**
     * 通过车次 id 获取该车次的途径站点集
     * @param trainId
     */
    private List<TrainSites> getSameIdTrainSitesListByTrainId(Integer trainId){
        List<TrainSites> sameIdTrainSitesList = trainSitesService.list(new QueryWrapper<TrainSites>().eq("id", trainId));
        // 排个序
        Collections.sort(sameIdTrainSitesList, Comparator.comparing(TrainSites::getOrder));
        return sameIdTrainSitesList;
    }

}
